﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using Djs.Games.DeskGame.GameComponents;
using Djs.WinForm.Components;

namespace Djs.Games.DeskGame.GameDefinitions
{
    #region class LudoGame : definice hry Člověče nezlob se (anglický název = LUDO)
    /// <summary>
    /// LudoGame : definice hry Člověče nezlob se (anglický název = LUDO)
    /// </summary>
    public class LudoGame : Game
    {
        #region Příprava, Reset
        /// <summary>
        /// Implementace abstraktní metody, volá se při tvorbě definice konkrétní hry, po jejím konstruktoru.
        /// Účel: naplnit konstantní prvky (typicky this.Cells).
        /// </summary>
        /// <param name="gameName"></param>
        public override void PrepareDefinition(string gameName)
        {
            this.PrepareCells(gameName);
        }
        /// <summary>
        /// Implementace abstraktní metody, volá se kdykoliv když je třeba hru vrátit do výchozí pozice.
        /// </summary>
        public override void Reset()
        {
            base.Reset();
            this.Start();
        }
        #endregion
        #region Popis aktuální hry a aktuálního stavu hry, pravidla - property neměnné v průběhu jedné hry
        /// <summary>
        /// Soupis aktuálních hráčů, v pořadí v jakém se střídají.
        /// </summary>
        public LudoGamePlayer[] CurrentPlayers { get; protected set; }
        /// <summary>
        /// Počet hráčů - nejvyšší v aktuální variantě hry
        /// </summary>
        public int GamePlayerCount { get { return this.CurrentPlayers.Length; } }
        /// <summary>
        /// Počet figurek na jednoho hráče - v aktuální variantě hry
        /// </summary>
        public int GamePineCount { get; protected set; }
        /// <summary>
        /// Rozměr políčka (v obou směrech stejný), logická hodnota
        /// </summary>
        protected float CellSize { get; set; }
        /// <summary>
        /// Vzdálenost sousedních políček, logická hodnota
        /// </summary>
        protected float CellDist { get; set; }
        /// <summary>
        /// Typ hrací plochy
        /// </summary>
        public LudoGameAreaType GameAreaType { get; protected set; }
        #endregion
        #region Deklarace hracích polí
        /// <summary>
        /// Zde třída Ludo určí, zda umí hrát hru (daného názvu).
        /// Pokud ano, bude vytvořena její instance a bude vyvolána instanční metoda PrepareDefinition().
        /// To zajišťuje ClassFactory metoda GameDefinition.GetGameDefinition().
        /// </summary>
        /// <param name="gameName"></param>
        /// <returns></returns>
        public static bool ImplementsGame(string gameName)
        {
            if (String.IsNullOrEmpty(gameName)) return false;
            return (gameName == "Ludo4a" || gameName == "Ludo4b" ||
                    gameName == "Ludo6a" || gameName == "Ludo6b" || gameName == "Ludo6c" || gameName == "Ludo6d" || 
                    gameName == "LudoEx");
        }
        /// <summary>
        /// Připraví hrací pole pro danou hru
        /// </summary>
        /// <param name="gameName"></param>
        protected virtual void PrepareCells(string gameName)
        {
            switch (gameName)
            {
                case "Ludo4a":
                    this.GameAreaType = LudoGameAreaType.Ludo4a;
                    this.PrepareCellsLudo4();
                    break;
                case "Ludo4b":
                    this.GameAreaType = LudoGameAreaType.Ludo4b;
                    this.PrepareCellsLudo4();
                    break;
                case "Ludo6a":
                    this.GameAreaType = LudoGameAreaType.Ludo6a;
                    this.PrepareCellsLudo6();
                    break;
                case "Ludo6b":
                    this.GameAreaType = LudoGameAreaType.Ludo6b;
                    this.PrepareCellsLudo6();
                    break;
                case "Ludo6c":
                    this.GameAreaType = LudoGameAreaType.Ludo6c;
                    this.PrepareCellsLudo6();
                    break;
                case "Ludo6d":
                    this.GameAreaType = LudoGameAreaType.Ludo6d;
                    this.PrepareCellsLudo6();
                    break;
                case "LudoEx":
                    this.GameAreaType = LudoGameAreaType.LudoExcentric;
                    this.PrepareCellsLudoEx();
                    break;
            }
        }
        /// <summary>
        /// Připraví hrací pole pro hru Ludo 4 (Člověče nezlob se pro 4 osoby)
        /// </summary>
        private void PrepareCellsLudo4()
        {
            // Hráči:
            this.CurrentPlayers = new LudoGamePlayer[4];
            this.GamePineCount = (this.GameAreaType == LudoGameAreaType.Ludo4b ? 3 : 4);

            // Hrací pole
            this.OuterBorder = 4f;
            this.CellSize = 7f;
            this.CellDist = 9f;
            MovablePoint point = new MovablePoint(-100, 0);
            int[] startCells = new int[4];               // Soupis indexů políček, která jsou nasazovací pro dané hráče
            int[] lastCells = new int[4];                // Soupis indexů políček, která jsou poslední před domečkem pro dané hráče
            int index = 0;

            // Od zeleného nasazovacího políčka:
            this.AddCellStart(0, ref index, point, Direct12.H12);
            startCells[0] = this.LastCellIndex;
            this.AddCellGray4(ref index, point, Direct12.H3, Direct12.H12);
            lastCells[1] = this.LastCellIndex;

            // Od červeného nasazovacího políčka:
            this.AddCellStart(1, ref index, point, Direct12.H3);
            startCells[1] = this.LastCellIndex;
            this.AddCellGray4(ref index, point, Direct12.H6, Direct12.H3);
            lastCells[2] = this.LastCellIndex;

            // Od modrého nasazovacího políčka:
            this.AddCellStart(2, ref index, point, Direct12.H6);
            startCells[2] = this.LastCellIndex;
            this.AddCellGray4(ref index, point, Direct12.H9, Direct12.H6);
            lastCells[3] = this.LastCellIndex;

            // Od žlutého nasazovacího políčka:
            this.AddCellStart(3, ref index, point, Direct12.H9);
            startCells[3] = this.LastCellIndex;
            this.AddCellGray4(ref index, point, Direct12.H12, Direct12.H9);
            lastCells[0] = this.LastCellIndex;

            // Linky:
            this.CreateLinkChain(true, this.CreateLudoLink);

            // Domečky a chlívečky:
            float innerDistRatio = ((this.GameAreaType == LudoGameAreaType.Ludo4a || this.GameAreaType == LudoGameAreaType.Ludo4b) ? 0.9f : 1.0f);
            this.AddCellHome(0, startCells[0], Direct12.H12, Direct12.H3, Direct12.H6);
            this.AddCellTarget(0, lastCells[0], innerDistRatio, Direct12.H3);
            this.AddCellHome(1, startCells[1], Direct12.H3, Direct12.H6, Direct12.H9);
            this.AddCellTarget(1, lastCells[1], innerDistRatio, Direct12.H6);
            this.AddCellHome(2, startCells[2], Direct12.H6, Direct12.H9, Direct12.H12);
            this.AddCellTarget(2, lastCells[2], innerDistRatio, Direct12.H9);
            this.AddCellHome(3, startCells[3], Direct12.H9, Direct12.H12, Direct12.H3);
            this.AddCellTarget(3, lastCells[3], innerDistRatio, Direct12.H12);

            // Figurky:
            float pineSizeRatio = 0.85f;
            this.AddPines(0, pineSizeRatio);
            this.AddPines(1, pineSizeRatio);
            this.AddPines(2, pineSizeRatio);
            this.AddPines(3, pineSizeRatio);
        }
        /// <summary>
        /// Připraví hrací pole pro hru Ludo 6 (Člověče nezlob se pro 6 osob)
        /// </summary>
        private void PrepareCellsLudo6()
        {
            // Hráči:
            this.CurrentPlayers = new LudoGamePlayer[6];
            this.GamePineCount = 4;
            this.CellSize = 7f;
            this.CellDist = 8f;

            // Hrací pole
            this.OuterBorder = 4f;
            MovablePoint point = new MovablePoint(-100, 0);
            int[] startCells = new int[6];               // Soupis indexů políček, která jsou nasazovací pro dané hráče
            int[] lastCells = new int[6];                // Soupis indexů políček, která jsou poslední před domečkem pro dané hráče
            int index = 0;

            //  Od zeleného nasazovacího políčka:
            this.AddCellStart(0, ref index, point, Direct12.H12);
            startCells[0] = this.LastCellIndex;
            this.AddCellGray6(ref index, point, Direct12.H3, Direct12.H1, Direct12.H11, Direct12.H2);
            lastCells[1] = this.LastCellIndex;

            //  Od modrého nasazovacího políčka:
            this.AddCellStart(1, ref index, point, Direct12.H2);
            startCells[1] = this.LastCellIndex;
            this.AddCellGray6(ref index, point, Direct12.H5, Direct12.H3, Direct12.H1, Direct12.H4);
            lastCells[2] = this.LastCellIndex;

            // Od dalšího políčka:
            this.AddCellStart(2, ref index, point, Direct12.H4);
            startCells[2] = this.LastCellIndex;
            this.AddCellGray6(ref index, point, Direct12.H7, Direct12.H5, Direct12.H3, Direct12.H6);
            lastCells[3] = this.LastCellIndex;

            // Další:
            this.AddCellStart(3, ref index, point, Direct12.H6);
            startCells[3] = this.LastCellIndex;
            this.AddCellGray6(ref index, point, Direct12.H9, Direct12.H7, Direct12.H5, Direct12.H8);
            lastCells[4] = this.LastCellIndex;

            // Další:
            this.AddCellStart(4, ref index, point, Direct12.H8);
            startCells[4] = this.LastCellIndex;
            this.AddCellGray6(ref index, point, Direct12.H11, Direct12.H9, Direct12.H7, Direct12.H10);
            lastCells[5] = this.LastCellIndex;

            // Další:
            this.AddCellStart(5, ref index, point, Direct12.H10);
            startCells[5] = this.LastCellIndex;
            this.AddCellGray6(ref index, point, Direct12.H1, Direct12.H11, Direct12.H9, Direct12.H12);
            lastCells[0] = this.LastCellIndex;

            // Linky:
            this.CreateLinkChain(true, this.CreateLudoLink);

            // Domečky a chlívečky:
            float innerDistRatio = 1f;
            this.AddCellHome(0, startCells[0], Direct12.H12, Direct12.H3, Direct12.H6);
            this.AddCellTarget(0, lastCells[0], innerDistRatio, Direct12.H3);
            this.AddCellHome(1, startCells[1], Direct12.H2, Direct12.H5, Direct12.H8);
            this.AddCellTarget(1, lastCells[1], innerDistRatio, Direct12.H5);
            this.AddCellHome(2, startCells[2], Direct12.H4, Direct12.H7, Direct12.H10);
            this.AddCellTarget(2, lastCells[2], innerDistRatio, Direct12.H7);
            this.AddCellHome(3, startCells[3], Direct12.H6, Direct12.H9, Direct12.H12);
            this.AddCellTarget(3, lastCells[3], innerDistRatio, Direct12.H9);
            this.AddCellHome(4, startCells[4], Direct12.H8, Direct12.H11, Direct12.H2);
            this.AddCellTarget(4, lastCells[4], innerDistRatio, Direct12.H11);
            this.AddCellHome(5, startCells[5], Direct12.H10, Direct12.H1, Direct12.H4);
            this.AddCellTarget(5, lastCells[5], innerDistRatio, Direct12.H1);

            // Figurky:
            float pineSizeRatio = 0.85f;
            this.AddPines(0, pineSizeRatio);
            this.AddPines(1, pineSizeRatio);
            this.AddPines(2, pineSizeRatio);
            this.AddPines(3, pineSizeRatio);
            this.AddPines(4, pineSizeRatio);
            this.AddPines(5, pineSizeRatio);
        }
        /// <summary>
        /// Vloží políčko typu Start
        /// </summary>
        /// <param name="player"></param>
        /// <param name="index"></param>
        /// <param name="point"></param>
        /// <param name="dist"></param>
        /// <param name="size"></param>
        /// <param name="d1"></param>
        private void AddCellStart(int player, ref int index, MovablePoint point, Direct12 d1)
        {
            LudoGamePlayer ludoPlayer = new LudoGamePlayer(this, player);
            LudoGameCell startCell = new LudoGameCell(LudoGameCellType.Start, player, index++, point.Move(d1, this.CellDist), this.CellSize, ImageColorPlayer(player), ImageNameCellStartPlayer(player));
            ludoPlayer.StartCell = startCell;
            this.AddCell(startCell);
            this.CurrentPlayers[player] = new LudoGamePlayer(this, player);
        }
        /// <summary>
        /// Přidá políčka výchozí (domeček)
        /// </summary>
        /// <param name="point">Výchozí políčko = políčko nasazovací</param>
        /// <param name="dist">Vzdálenost políček</param>
        /// <param name="space">Vzdálenost domečku od nasazovacího políčka v počtu dist (typicky 2 nebo 3)</param>
        /// <param name="size">Velikost políčka</param>
        /// <param name="backColor"></param>
        /// <param name="imageCell"></param>
        /// <param name="d1">Směr první (2 kroky k prvnímu políčku, jeden krok k druhému políčku)</param>
        /// <param name="d2">Směr druhý (krok ke třetímu políčku)</param>
        /// <param name="d3">Směr třetí (krok ke čtvrtému políčku)</param>
        private void AddCellHome(int player, int startCellIndex, Direct12 d1, Direct12 d2, Direct12 d3)
        {
            // Políčka reprezentující domeček:
            LudoGamePlayer ludoPlayer = this.CurrentPlayers[player];
            MovablePoint point = new MovablePoint(this.Cells[startCellIndex].LogicalCenter);
            Color backColor = ImageColorPlayer(player);
            string imageCell = ImageNameCellHomePlayer(player);
            int itemCount = this.GamePineCount;
            for (int index = 0; index < itemCount; index++)
            {
                Direct12 direct = ((index == 0 || index == 1) ? d1 : (index == 2) ? d2 : d3);
                float currDist = (index == 0 ? this.HomeCellsSpace * this.CellDist : this.CellDist);
                LudoGameCell homeCell = new LudoGameCell(LudoGameCellType.Home, player, index, point.Move(direct, currDist), this.CellSize, backColor, imageCell);
                ludoPlayer.HomeCells.Add(homeCell);
                this.AddCell(homeCell);
            }

            // Randomizer:
            GameRand rand = new GameRand() { LogicalCenter = point.Move(d3, this.CellDist), LogicalSize = new SizeF(this.CellSize, this.CellSize) };
            ludoPlayer.Rand = rand;
            this.AddRand(rand);
        }
        /// <summary>
        /// Přidá políčka cílová (chlíveček)
        /// </summary>
        /// <param name="lastCellIndex">Index políčka posledního těsně před prvním Target</param>
        /// <param name="d1">Směr první (1 kroky k prvnímu políčku směr 1)</param>
        /// <param name="d1">Směr druhý (kroky mezi políčky)</param>
        private void AddCellTarget(int player, int lastCellIndex, float innerDistRatio, Direct12 d1)
        {
            LudoGamePlayer ludoPlayer = this.CurrentPlayers[player];
            MovablePoint point = new MovablePoint(this.Cells[lastCellIndex].LogicalCenter);
            Color backColor = ImageColorPlayer(player);
            string imageCell = ImageNameCellTargetPlayer(player);
            List<int> indexes = new List<int>();
            indexes.Add(lastCellIndex);
            int itemCount = this.GamePineCount;
            float dist = this.CellDist * innerDistRatio;
            for (int index = 0; index < itemCount; index++)
            {
                LudoGameCell targetCell = new LudoGameCell(LudoGameCellType.Target, player, index, point.Move(d1, dist), this.CellSize, backColor, imageCell);
                ludoPlayer.TargetCells.Add(targetCell);
                this.AddCell(targetCell);
                indexes.Add(this.LastCellIndex);
            }
            this.CreateLinkChain(this.CreateLudoLink, indexes);
        }
        /// <summary>
        /// Přidá políčka běžná šedá, tvar pole pro 4 hráče
        /// </summary>
        /// <param name="pt">Výchozí políčko = políčko nasazovací</param>
        /// <param name="d1">Směr první (4 políčka a na konci jedno políčko)</param>
        /// <param name="d2">Směr druhý (4 políčka)</param>
        private void AddCellGray4(ref int index, MovablePoint pt, Direct12 d1, Direct12 d2)
        {
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, -1, index++, pt.Move(d1, this.CellDist), this.CellSize, Color.LightGray, ImageNameCellStandard));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, -1, index++, pt.Move(d1, this.CellDist), this.CellSize, Color.LightGray, ImageNameCellStandard));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, -1, index++, pt.Move(d1, this.CellDist), this.CellSize, Color.LightGray, ImageNameCellStandard));

            switch (this.GameAreaType)
            {
                case LudoGameAreaType.Ludo4a:
                    this.AddCell(new LudoGameCell(LudoGameCellType.Standard, -1, index++, pt.Move(d1, this.CellDist), this.CellSize, Color.LightGray, ImageNameCellStandard));
                    this.AddCell(new LudoGameCell(LudoGameCellType.Standard, -1, index++, pt.Move(d2, this.CellDist), this.CellSize, Color.LightGray, ImageNameCellStandard));
                    break;
            }
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, -1, index++, pt.Move(d2, this.CellDist), this.CellSize, Color.LightGray, ImageNameCellStandard));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, -1, index++, pt.Move(d2, this.CellDist), this.CellSize, Color.LightGray, ImageNameCellStandard));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, -1, index++, pt.Move(d2, this.CellDist), this.CellSize, Color.LightGray, ImageNameCellStandard));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, -1, index++, pt.Move(d1, this.CellDist), this.CellSize, Color.LightGray, ImageNameCellStandard));
        }
        /// <summary>
        /// Přidá políčka běžná šedá, tvar pole pro 6 hráčů
        /// </summary>
        /// <param name="point">Výchozí políčko = políčko nasazovací</param>
        /// <param name="this.CellDist">Vzdálenost políček</param>
        /// <param name="size">Velikost políčka</param>
        /// <param name="d1">Směr první (4 políčka)</param>
        /// <param name="d2">Směr druhý (2 políčka)</param>
        /// <param name="d3">Směr třetí (4 políčka)</param>
        /// <param name="d4">Směr čtvrtý (1 políčko)</param>
        private void AddCellGray6(ref int index, MovablePoint point, Direct12 d1, Direct12 d2, Direct12 d3, Direct12 d4)
        {
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, -1, index++, point.Move(d1, this.CellDist), this.CellSize, Color.LightGray, ImageNameCellStandard));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, -1, index++, point.Move(d1, this.CellDist), this.CellSize, Color.LightGray, ImageNameCellStandard));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, -1, index++, point.Move(d1, this.CellDist), this.CellSize, Color.LightGray, ImageNameCellStandard));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, -1, index++, point.Move(d1, this.CellDist), this.CellSize, Color.LightGray, ImageNameCellStandard));

            switch (this.GameAreaType)
            {
                case LudoGameAreaType.Ludo6b:
                    this.AddCell(new LudoGameCell(LudoGameCellType.Standard, -1, index++, point.Move(d1, this.CellDist), this.CellSize, Color.LightGray, ImageNameCellStandard));
                    this.AddCell(new LudoGameCell(LudoGameCellType.Standard, -1, index++, point.Move(d3, this.CellDist), this.CellSize, Color.LightGray, ImageNameCellStandard));
                    break;
                case LudoGameAreaType.Ludo6c:
                    this.AddCell(new LudoGameCell(LudoGameCellType.Standard, -1, index++, point.Move(d2, this.CellDist), this.CellSize, Color.LightGray, ImageNameCellStandard));
                    break;
                case LudoGameAreaType.Ludo6d:
                    break;
                case LudoGameAreaType.Ludo6a:
                default:
                    this.AddCell(new LudoGameCell(LudoGameCellType.Standard, -1, index++, point.Move(d2, this.CellDist), this.CellSize, Color.LightGray, ImageNameCellStandard));
                    this.AddCell(new LudoGameCell(LudoGameCellType.Standard, -1, index++, point.Move(d2, this.CellDist), this.CellSize, Color.LightGray, ImageNameCellStandard));
                    break;
            }
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, -1, index++, point.Move(d3, this.CellDist), this.CellSize, Color.LightGray, ImageNameCellStandard));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, -1, index++, point.Move(d3, this.CellDist), this.CellSize, Color.LightGray, ImageNameCellStandard));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, -1, index++, point.Move(d3, this.CellDist), this.CellSize, Color.LightGray, ImageNameCellStandard));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, -1, index++, point.Move(d3, this.CellDist), this.CellSize, Color.LightGray, ImageNameCellStandard));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, -1, index++, point.Move(d4, this.CellDist), this.CellSize, Color.LightGray, ImageNameCellStandard));
        }
        /// <summary>
        /// Přidá figurky do všech políček, která jsou typu Home pro daného hráče.
        /// </summary>
        /// <param name="player"></param>
        /// <param name="pineSizeRatio"></param>
        private void AddPines(int player, float pineSizeRatio)
        {
            LudoGamePlayer ludoPlayer = this.CurrentPlayers[player];
            List<LudoGameCell> homeCells = this.FindLudoCell(LudoGameCellType.Home, player);
            float pineSize = pineSizeRatio * this.CellSize;
            SizeF sizeF = new SizeF(pineSize, pineSize);
            foreach (LudoGameCell homeCell in homeCells)
            {
                LudoGamePine ludoPine = new LudoGamePine(homeCell, sizeF);
                ludoPlayer.Pines.Add(ludoPine);
                this.AddPine(ludoPine);
            }
        }
        /// <summary>
        /// Vytvoří a vrátí objekt třídy LudoGameLink pro dva objekty LudoGameCell.
        /// Deklarativní typy jsou obecnější, metoda tak splňuje podmínky předání do metod GameDefinition.CreateLinkChain().
        /// </summary>
        /// <param name="cell1"></param>
        /// <param name="cell2"></param>
        /// <returns></returns>
        private GameLink CreateLudoLink(GameCell cell1, GameCell cell2)
        {
            LudoGameCell ludoCell1 = cell1 as LudoGameCell;
            LudoGameCell ludoCell2 = cell2 as LudoGameCell;
            Color lineColor = Color.DimGray;
            int player = -1;
            if (ludoCell1 != null && ludoCell1.CellType == LudoGameCellType.Target && ludoCell1.Player >= 0)
                player = ludoCell1.Player;
            else if (ludoCell2 != null && ludoCell2.CellType == LudoGameCellType.Target && ludoCell2.Player >= 0)
                player = ludoCell2.Player;
            if (player >= 0)
                lineColor = LudoGame.ImageColorPlayer(player);

            return new LudoGameLink() { Cell1 = cell1, Cell2 = cell2, DrawLink = true, LogicalWidth = 1f, LineColor = lineColor };
        }
        /// <summary>
        /// Vzdálenost domečku od nasazovacího políčka, v počtu buněk (1 = vzdálenost dvou sousedních políček)
        /// </summary>
        protected float HomeCellsSpace
        {
            get
            {
                switch (this.GameAreaType)
                {
                    case LudoGameAreaType.Ludo4a: return 3.0f;
                    case LudoGameAreaType.Ludo4b: return 2.0f;
                    case LudoGameAreaType.Ludo6a: return 2.5f;
                    case LudoGameAreaType.Ludo6b: return 2.2f;
                    case LudoGameAreaType.Ludo6c: return 2.2f;
                    case LudoGameAreaType.Ludo6d: return 2.2f;
                }
                return 2f;
            }
        }
        /// <summary>
        /// Třída, která umožňuje vytvořit bod, který se pohybuje po stanovaných krocích v úhlech po 30 stupních (vyjádřené v hodinách).
        /// </summary>
        protected class MovablePoint
        {
            public MovablePoint()
            {
                this._CoefL = ((float)Math.Sqrt(7500d)) / 100f;
                this._CoefS = 0.500f;
                this.CurrentPoint = new PointF(0, 0);
            }
            public MovablePoint(float x, float y)
            {
                this._CoefL = ((float)Math.Sqrt(7500d)) / 100f;
                this._CoefS = 0.500f;
                this.CurrentPoint = new PointF(x, y);
            }
            public MovablePoint(PointF point)
            {
                this._CoefL = ((float)Math.Sqrt(7500d)) / 100f;
                this._CoefS = 0.500f;
                this.CurrentPoint = new PointF(point.X, point.Y);
            }
            public MovablePoint Clone()
            {
                MovablePoint clone = (MovablePoint)this.MemberwiseClone();
                return clone;
            }
            /// <summary>
            /// Délka delší odvěsny nad pravoúhlým trojúhelníkem s úhlem 60 stupňů a s přeponou = 1f.  Je zde hodnota 0.866...
            /// Kratší odvěsna je dlouhá 0.50f, je v proměnné _CoefS :-)
            /// </summary>
            private float _CoefL;
            /// <summary>
            /// Délka kratší odvěsny nad pravoúhlým trojúhelníkem s úhlem 60 stupňů a s přeponou = 1f.  Je zde hodnota 0.500f
            /// Delší odvěsna je dlouhá 0.866...f, je v proměnné _CoefL :-)
            /// </summary>
            private float _CoefS;
            public PointF CurrentPoint;
            public PointF Move(Direct12 direction, float dist)
            {
                switch (direction)
                {
                    case Direct12.H1:
                        this.CurrentPoint.X += this._CoefS * dist;
                        this.CurrentPoint.Y -= this._CoefL * dist;
                        break;
                    case Direct12.H2:
                        this.CurrentPoint.X += this._CoefL * dist;
                        this.CurrentPoint.Y -= this._CoefS * dist;
                        break;
                    case Direct12.H3:
                        this.CurrentPoint.X += dist;
                        break;
                    case Direct12.H4:
                        this.CurrentPoint.X += this._CoefL * dist;
                        this.CurrentPoint.Y += this._CoefS * dist;
                        break;
                    case Direct12.H5:
                        this.CurrentPoint.X += this._CoefS * dist;
                        this.CurrentPoint.Y += this._CoefL * dist;
                        break;
                    case Direct12.H6:
                        this.CurrentPoint.Y += dist;
                        break;
                    case Direct12.H7:
                        this.CurrentPoint.X -= this._CoefS * dist;
                        this.CurrentPoint.Y += this._CoefL * dist;
                        break;
                    case Direct12.H8:
                        this.CurrentPoint.X -= this._CoefL * dist;
                        this.CurrentPoint.Y += this._CoefS * dist;
                        break;
                    case Direct12.H9:
                        this.CurrentPoint.X -= dist;
                        break;
                    case Direct12.H10:
                        this.CurrentPoint.X -= this._CoefL * dist;
                        this.CurrentPoint.Y -= this._CoefS * dist;
                        break;
                    case Direct12.H11:
                        this.CurrentPoint.X -= this._CoefS * dist;
                        this.CurrentPoint.Y -= this._CoefL * dist;
                        break;
                    case Direct12.H12:
                        this.CurrentPoint.Y -= dist;
                        break;
                }
                return this.CurrentPoint;
            }
        }
        protected enum Direct12
        {
            None = 0,
            /// <summary>Směr 1 hodina</summary>
            H1,
            /// <summary>Směr 2 hodiny</summary>
            H2,
            /// <summary>Směr 3 hodiny</summary>
            H3,
            /// <summary>Směr 4 hodiny</summary>
            H4,
            /// <summary>Směr 5 hodin</summary>
            H5,
            /// <summary>Směr 6 hodin</summary>
            H6,
            /// <summary>Směr 7 hodin</summary>
            H7,
            /// <summary>Směr 8 hodin</summary>
            H8,
            /// <summary>Směr 9 hodin</summary>
            H9,
            /// <summary>Směr 10 hodin</summary>
            H10,
            /// <summary>Směr 11 hodin</summary>
            H11,
            /// <summary>Směr 12 hodin</summary>
            H12
        }
        #endregion
        #region Excentrické Ludo
        private void PrepareCellsLudoEx()
        {
            int index = 0;
            this.OuterBorder = 10f;
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 10, 10, 7, Color.LightYellow, "circle_yellow.png"));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 20, 10, 7, Color.LightYellow, "circle_yellow.png"));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 28, 15, 7, Color.LightYellow, "circle_yellow.png"));
            int joinTo = this.LastCellIndex;
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 35, 22, 7, Color.LightYellow, "circle_yellow.png"));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 40, 30, 8, Color.LightYellow, "circle_yellow.png"));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 40, 40, 12, Color.LightYellow, "circle_yellow.png") { ForeStdImageName = "animals-bat.png" });
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 45, 48, 8, Color.LightYellow, "circle_purple.png"));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 52, 52, 7, Color.LightYellow, "circle_purple.png"));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 60, 48, 6, Color.LightYellow, "circle_purple.png"));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 65, 40, 5, Color.LightYellow, "circle_purple.png"));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 72, 32, 5, Color.LightYellow, "circle_red.png"));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 76, 21, 5, Color.LightYellow, "circle_red.png"));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 79, 10, 6, Color.LightYellow, "circle_red.png"));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 79, 0, 6, Color.LightYellow, "circle_red.png"));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 77, -3, 12, Color.LightYellow, "circle_tan.png") { ForeStdImageName = "animals-frog-poison_dart.png" });
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 73, -8, 8, Color.LightYellow, "circle_tan.png"));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 68, -14, 9, Color.LightYellow, "circle_tan.png"));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 61, -22, 10, Color.LightYellow, "circle_tan.png"));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 52, -28, 12, Color.LightYellow, "circle_tan.png") { ForeStdImageName = "animals-eagle_face.png" });
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 41, -33, 10, Color.LightYellow, "circle_tan.png"));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 30, -35, 9, Color.LightYellow, "circle_green.png"));
            int joinFrom = this.LastCellIndex;
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 20, -31, 8, Color.LightYellow, "circle_green.png"));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 11, -25, 8, Color.LightYellow, "circle_green.png"));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 3, -20, 8, Color.LightYellow, "circle_green.png"));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, -5, -14, 12, Color.LightYellow, "circle_green.png") { ForeStdImageName = "ghost-blue.png" });
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, -12, -6, 7, Color.LightYellow, "circle_blue.png"));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, -17, 1, 7, Color.LightYellow, "circle_blue.png"));

            this.CreateLinkChain(false);        // Spojí linkem všechny dosavadní prvky

            // Zkratka:
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 33, -27, 7, Color.LightYellow, ImageNameCellStandard));
            int join1 = this.LastCellIndex;
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 34, -19, 7, Color.LightYellow, ImageNameCellStandard));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 34, -11, 7, Color.LightYellow, ImageNameCellStandard));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 32, -3, 7, Color.LightYellow, ImageNameCellStandard));
            this.AddCell(new LudoGameCell(LudoGameCellType.Standard, 0, index++, 29, 4, 7, Color.LightYellow, ImageNameCellStandard));
            int join2 = this.LastCellIndex;

            // Vytvořím linky pro zkratku:
            this.CreateLinkChain(joinFrom, join1, join1 + 1, join1 + 2, join1 + 3, join1 + 4, joinTo);
        }
        #endregion
        #region Vyhledání políčka podle jeho vlastností, vyhledání figurky
        /// <summary>
        /// Najde a vrátí soupis všech buněk daného typu.
        /// Další funkce v nich mohou filtrovat.
        /// </summary>
        /// <param name="cellType"></param>
        /// <returns></returns>
        protected List<LudoGameCell> FindLudoCell(LudoGameCellType cellType)
        {
            return this.Cells.OfType<LudoGameCell>().Where(c => c.CellType == cellType).ToList();
        }
        /// <summary>
        /// Najde a vrátí soupis buněk daného typu pro daného hráče.
        /// Buňky typu LudoGameCellType.Standard jsou pro hráče s číslem -1.
        /// Počet hráčů je uložen v this.GamePlayerCount
        /// </summary>
        /// <param name="cellType"></param>
        /// <param name="player"></param>
        /// <returns></returns>
        protected List<LudoGameCell> FindLudoCell(LudoGameCellType cellType, int player)
        {
            return this.FindLudoCell(cellType).FindAll(c => c.Player == player);
        }
        /// <summary>
        /// Najde políčko podle jeho přesných vlastností (typ, hráč, index).
        /// Takové by mělo být jen jedno.
        /// Pokud nenajde, vrátí null.
        /// </summary>
        /// <param name="cellType"></param>
        /// <param name="player"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        protected LudoGameCell FindLudoCell(LudoGameCellType cellType, int player, int index)
        {
            return this.FindLudoCell(cellType).FirstOrDefault(c => c.Player == player && c.Index == index);
        }
        /// <summary>
        /// Najde a vrátí (první) figurku, která stojí na daném políčku. Nebo vrátí null.
        /// </summary>
        /// <param name="cell"></param>
        /// <returns></returns>
        protected LudoGamePine FindLudoPineOnCell(GameCell cell)
        {
            List<GamePine> cells = this.FindPinesOnCell(cell);
            if (cells == null || cells.Count == 0) return null;
            return cells[0] as LudoGamePine;
        }
        /// <summary>
        /// Najde a vrátí všechna cílová políčka (GameCell), která jsou přímo linkovaná s výchozím políčkem, v daném směru.
        /// Pokud není zadané políčko, vrátí null.
        /// Pro směr Next hledá vztahy, kde dané políčko cell je ve vztahu v property Cell1 (výchozí).
        /// Pro směr Prev hledá vztahy, kde dané políčko cell je ve vztahu v property Cell2 (cílové).
        /// </summary>
        /// <param name="cell">Výchozí políčko</param>
        /// <param name="direction">Směr</param>
        /// <returns></returns>
        protected List<LudoGameCell> FindLudoCellsLinkedToCell(GameCell cell, GameMoveDirection direction)
        {
            List<GameCell> cells = this.FindCellsLinkedToCell(cell, direction);
            if (cells == null) return null;
            return cells.OfType<LudoGameCell>().ToList();
        }
        #endregion
        #region Konstanty a statické metody, určující používané obrázky pro hru
        /// <summary>
        /// Název obrázku pro políčko: 
        /// </summary>
        public static string ImageNameCellStandard { get { return "circle_grey.png"; } }
        /// <summary>
        /// Název obrázku pro políčko: Domeček (název obsahuje proměnnou %0 na místě barvy, která odpovídá hráči).
        /// </summary>
        public static string ImageNameCellHome { get { return "circle_%0.png"; } }
        /// <summary>
        /// Název obrázku pro políčko: Nasazovací (název obsahuje proměnnou %0 na místě barvy, která odpovídá hráči).
        /// </summary>
        public static string ImageNameCellStart { get { return "circle_%0.png"; } }
        /// <summary>
        /// Název obrázku pro políčko: Cíl (název obsahuje proměnnou %0 na místě barvy, která odpovídá hráči).
        /// </summary>
        public static string ImageNameCellTarget { get { return "circle_%0.png"; } }
        /// <summary>
        /// Název obrázku pro figurku: v klidu (název obsahuje proměnnou %0 na místě barvy, která odpovídá hráči).
        /// </summary>
        public static string ImageNamePineStandard { get { return "face-embarrassed.png"; } }
        /// <summary>
        /// Název obrázku pro figurku: s myší, bez pohybu (název obsahuje proměnnou %0 na místě barvy, která odpovídá hráči).
        /// </summary>
        public static string ImageNamePineMouse { get { return "face-plain.png"; } }
        /// <summary>
        /// Název obrázku pro figurku: v pohybu mimo políčka (název obsahuje proměnnou %0 na místě barvy, která odpovídá hráči).
        /// </summary>
        public static string ImageNamePineMoveOut { get { return "face-smile.png"; } }
        /// <summary>
        /// Název obrázku pro figurku: v pohybu nad prázdným dostupným políčkem (název obsahuje proměnnou %0 na místě barvy, která odpovídá hráči).
        /// </summary>
        public static string ImageNamePineMoveCellOk { get { return "face-smile-4.png"; } }
        /// <summary>
        /// Název obrázku pro figurku: v pohybu nad nesprávným políčkem (kam nesmím) (název obsahuje proměnnou %0 na místě barvy, která odpovídá hráči).
        /// </summary>
        public static string ImageNamePineMoveCellError { get { return "face-surprise.png"; } }
        /// <summary>
        /// Název obrázku pro figurku: v pohybu nad políčkem, odkud bych někoho vyhodil (název obsahuje proměnnou %0 na místě barvy, která odpovídá hráči).
        /// </summary>
        public static string ImageNamePineMoveCellWarning { get { return "face-laugh.png"; } }
        /// <summary>
        /// Název barvy pro hráče 0
        /// </summary>
        public static string ImageColorPlayer0 { get { return "green"; } }
        /// <summary>
        /// Název barvy pro hráče 1
        /// </summary>
        public static string ImageColorPlayer1 { get { return "red"; } }
        /// <summary>
        /// Název barvy pro hráče 2
        /// </summary>
        public static string ImageColorPlayer2 { get { return "blue"; } }
        /// <summary>
        /// Název barvy pro hráče 3
        /// </summary>
        public static string ImageColorPlayer3 { get { return "yellow"; } }
        /// <summary>
        /// Název barvy pro hráče 4
        /// </summary>
        public static string ImageColorPlayer4 { get { return "purple"; } }
        /// <summary>
        /// Název barvy pro hráče 5
        /// </summary>
        public static string ImageColorPlayer5 { get { return "brown"; } }
        public static string ImageNameCellHomePlayer(int player) { return ImageNameCellHome.Replace("%0", ImageNamePlayer(player)); }
        public static string ImageNameCellStartPlayer(int player) { return ImageNameCellStart.Replace("%0", ImageNamePlayer(player)); }
        public static string ImageNameCellTargetPlayer(int player) { return ImageNameCellTarget.Replace("%0", ImageNamePlayer(player)); }
        public static string ImageNamePineStandardPlayer(int player) { return ImageNamePineStandard.Replace("%0", ImageNamePlayer(player)); }
        public static string ImageNamePineMousePlayer(int player) { return ImageNamePineMouse.Replace("%0", ImageNamePlayer(player)); }
        public static string ImageNamePineMoveOutPlayer(int player) { return ImageNamePineMoveOut.Replace("%0", ImageNamePlayer(player)); }
        public static string ImageNamePineMoveCellOkPlayer(int player) { return ImageNamePineMoveCellOk.Replace("%0", ImageNamePlayer(player)); }
        public static string ImageNamePineMoveCellWarningPlayer(int player) { return ImageNamePineMoveCellWarning.Replace("%0", ImageNamePlayer(player)); }
        public static string ImageNamePineMoveCellErrorPlayer(int player) { return ImageNamePineMoveCellError.Replace("%0", ImageNamePlayer(player)); }
        public static string ImageNamePlayer(int player)
        {
            switch (player)
            {
                case 0: return ImageColorPlayer0;
                case 1: return ImageColorPlayer1;
                case 2: return ImageColorPlayer2;
                case 3: return ImageColorPlayer3;
                case 4: return ImageColorPlayer4;
                case 5: return ImageColorPlayer5;
            }
            return "";
        }
        public static string ImageNamePineTargetPlayer(int player, GameMoveResultType moveResult)
        {
            switch (moveResult)
            {
                case GameMoveResultType.None: return ImageNamePineStandardPlayer(player);
                case GameMoveResultType.CellOk: return ImageNamePineMoveCellOkPlayer(player);
                case GameMoveResultType.CellWarning: return ImageNamePineMoveCellWarningPlayer(player);
                case GameMoveResultType.CellError: return ImageNamePineMoveCellErrorPlayer(player);
            }
            return ImageNamePineStandardPlayer(player);
        }
        public static Color ImageColorPlayer(int player)
        {
            switch (player)
            {
                case 0: return Color.LightGreen;
                case 1: return Color.LightCoral;
                case 2: return Color.LightBlue;
                case 3: return Color.LightYellow;
                case 4: return Color.MediumPurple;
                case 5: return Color.SandyBrown;
            }
            return Color.LightGray;
        }
        #endregion
        #region Řízení stavu: start, přechod mezi hráči, řízení hry
        protected void Start()
        {
            // Kolik bude hrát lidí, jakých a v jakém pořadí:
            this.CurrentPlayers[0].Status = LudoGamePlayerStatus.IsHuman;
            this.CurrentPlayers[2].Status = LudoGamePlayerStatus.IsAlien;

            // Aktivuj prvního hráče, který je na řadě:
            this.CurrentPlayerIndex = -1;
            this.ActivateNextPlayer();
        }
        /// <summary>
        /// Aktivuje hráče, který je další na řadě
        /// </summary>
        /// <returns></returns>
        protected bool ActivateNextPlayer()
        {
            int currentOrder = (this.CurrentPlayerIndex >= 0 ? this.CurrentPlayer.Order : -1);
            
            LudoGamePlayer nextPlayer = this.CurrentPlayers.Where(p => p.CanPlay && p.Order > currentOrder).OrderBy(p => p.Order).FirstOrDefault();
            if (nextPlayer == null)
                nextPlayer = this.CurrentPlayers.Where(p => p.CanPlay).OrderBy(p => p.Order).FirstOrDefault();
            if (nextPlayer == null)
                return false;

            int nextIndex = this.CurrentPlayers.ToList().FindIndex(p => Object.ReferenceEquals(p, nextPlayer));
            if (nextIndex < 0)
                return false;

            this.ActivatePlayer(nextIndex);
            return true;
        }
        /// <summary>
        /// Aktivuje daného hráče
        /// </summary>
        /// <param name="player"></param>
        protected void ActivatePlayer(int player)
        {
            foreach (LudoGamePlayer ludoPlayer in this.CurrentPlayers)
            {
                bool isActive = (ludoPlayer.Player == player);
                ludoPlayer.IsActive = isActive;
                ludoPlayer.Rand.Visible = isActive;
            }
            this.CurrentPlayerIndex = player;
            this.OnReDrawLayer(2);
        }
        #endregion
        #region Řešení pravidel, vývoj hry, aktuální stav
        /// <summary>
        /// Index aktuálního hráče
        /// </summary>
        public int CurrentPlayerIndex { get; protected set; }
        /// <summary>
        /// Data aktuálního hráče
        /// </summary>
        protected LudoGamePlayer CurrentPlayer { get { return (this.CurrentPlayerIndex >= 0 && this.CurrentPlayerIndex < this.CurrentPlayers.Length ? this.CurrentPlayers[this.CurrentPlayerIndex] : null); } }
        /// <summary>
        /// Aktuální délka pohybu = odpovídá číslu, hozenému kostkou (typicky 1-6)
        /// </summary>
        protected int CurrentMoveLength { get; set; }
        /// <summary>
        /// true, pokud aktuální délka pohybu (CurrentMoveLength) opravňuje ke startu (z Home na Start)
        /// </summary>
        protected bool CurrentMoveLengthIsForStart { get { return this.CurrentMoveLength == 6; } }
        /// <summary>
        /// Zjistí, zda daná figurka je v tuto chvíli povolená pro interaktivní akce (MouseOver, MouseDrag).
        /// </summary>
        /// <param name="ludoPine"></param>
        /// <returns></returns>
        public bool IsPineCurrentActive(LudoGamePine ludoPine)
        {
            if (ludoPine == null) return false;                                // Neznám figurku
            if (ludoPine.LudoCell == null) return false;                       // Figurka je mimo políčko
            if (ludoPine.Player != this.CurrentPlayerIndex) return false;      // Hráč není na tahu
            if (this.CurrentMoveLength == 0) return false;                     // Hráč nehodil kostkou
            if (ludoPine.LudoCell.CellType == LudoGameCellType.Home && !this.CurrentMoveLengthIsForStart) return false;  // Figurka je v domečku, a hozené číslo není startovací
            return true;
        }
        /// <summary>
        /// Metoda zjistí, zda a jak daná figurka smí přijít na dané cílové políčko.
        /// </summary>
        /// <param name="ludoPine"></param>
        /// <param name="cell"></param>
        /// <param name="pines"></param>
        /// <returns></returns>
        public GameMoveResultType GetMoveResult(LudoGamePine ludoPine, GameCell cell, List<GamePine> pines)
        {
            LudoGameCell ludoCell = cell as LudoGameCell;
            if (ludoPine == null || ludoCell == null) return GameMoveResultType.OutOfCell;                    // Když nevím, tak nesmím
            if (!this.IsPineCurrentActive(ludoPine)) return GameMoveResultType.CellError;                     // Figurka se nesmí pohybovat
            if (Object.ReferenceEquals(ludoPine.Cell, ludoCell)) return GameMoveResultType.CellOk;            // Zpátky můžu vždycky.
            if (ludoCell.CellType == LudoGameCellType.Home) return GameMoveResultType.CellError;              // Do domečku (kromě cesty zpět) nesmím.
            if (ludoCell.CellType == LudoGameCellType.Target && ludoCell.Player != ludoPine.Player) return GameMoveResultType.CellError;  // Do cizího cíle nesmím

            // Prověřím, zda cílové políčko odpovídá tomu, kam mám aktuálně dojít (hodili jsme kostkou, a máme jít o Nnn políček):
            List<LudoGameCell> targetCells = this.GetCurrentTargetsForPine(ludoPine);
            if (targetCells == null || targetCells.Count == 0) return GameMoveResultType.CellError;           // Nemůžeme se pohybovat (například stojím v domečku a hodil jsem 5)
            if (!targetCells.Any(c => Object.ReferenceEquals(c, cell))) return GameMoveResultType.CellError;  // Můžeme se pohybovat, ale chtěl bych na políčko, kam jsem nehodil správné číslo

            // Ze seznamu figurek (GamePine) na cílovém políčku vytvořím seznam figurek typu LudoGamePine:
            List<LudoGamePine> ludoPines = (pines != null ? pines.OfType<LudoGamePine>().ToList() : new List<LudoGamePine>());
            
            // Pokud tam nikdo není, OK:
            if (ludoPines.Count == 0) return GameMoveResultType.CellOk;

            // Pokud na cílovém políčku je tentýž hráč, jehož je aktivní figurka, tak Error:
            if (ludoPines.Any(p => p.Player == ludoPine.Player)) return GameMoveResultType.CellError;
            
            // Někdo tam je a já to nejsem - Warning:
            return GameMoveResultType.CellWarning;
        }
        /// <summary>
        /// Vrátí cílové políčko pro danou figurku, podle pravidel a aktuálního stavu hry.
        /// Řeší se zde cesta vpřed (a cesta vzad, pokud jsem na políčku typu Cíl).
        /// Řeší se zde výchozí pozice (v domečku) a nasazovací číslo (6).
        /// Neřeší se zde obsazenost cílového políčka.
        /// </summary>
        /// <param name="ludoPine"></param>
        /// <returns></returns>
        protected List<LudoGameCell> GetCurrentTargetsForPine(LudoGamePine ludoPine)
        {
            List<LudoGameCell> result = new List<LudoGameCell>();

            // 1. Pokud hráč dané figurky není na tahu, nemá žádná cílová políčka:
            if (ludoPine.Player != this.CurrentPlayerIndex) return result;
            //   anebo pokud figurka není na žádném políčku:
            if (ludoPine.LudoCell == null) return result;

            // 2. Pokud se dosud nehodilo kostkou, pak není žádný cíl:
            if (this.CurrentMoveLength == 0) return result;

            LudoGameCell cell;
            // 3. Pokud je figurka v domečku (výchozí stav), tak je to jinak: z domečku nevedou vztahy!
            if (ludoPine.LudoCell.CellType == LudoGameCellType.Home)
            {
                // Pokud jsme hodili nasazovací číslo => pak cílové políčko je moje nasazovací:
                if (this.CurrentMoveLengthIsForStart)
                {
                    cell = this.FindLudoCell(LudoGameCellType.Start, ludoPine.Player).FirstOrDefault();
                    if (cell != null)
                        result.Add(cell);
                }
                return result;
            }

            // Ostatní pozice figurek jsou pohyblivé, a jsou dané přes vztahy políček:
            // 4. Smíme jít dopředu o daný počet políček:
            cell = this.GetTargetCellForMove(ludoPine.LudoCell, ludoPine.Player, this.CurrentMoveLength);
            if (cell != null)
                result.Add(cell);

            // 5. Pokud je figurka na políčku typu Cíl, pak může jít i zpátky, pokud ale i cílové políčko bude typu Cíl (pohyb v domečku):
            if (ludoPine.LudoCell.CellType == LudoGameCellType.Target)
            {
                cell = this.GetTargetCellForMove(ludoPine.LudoCell, ludoPine.Player, -this.CurrentMoveLength);
                if (cell != null && cell.CellType == LudoGameCellType.Target)
                    result.Add(cell);
            }
            return result;
        }
        /// <summary>
        /// Najde a vrátí políčko, které leží v dané vzdálenosti (length) od políčka výchozího (ludoCell), pro hráče (player).
        /// Vzdálenost může být kladná i záporná.
        /// Pokud je vzdálenost == 0, vrací objekt ludoCell.
        /// Pokud je na vstupu ludoCell == null, vrací null.
        /// Pokud v dané vzdálenosti už nic neleží, vrací null (například stojím před cílem a mám vzdálenost +6, tam už žádná buňka není).
        /// </summary>
        /// <param name="ludoCell"></param>
        /// <param name="player"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        protected LudoGameCell GetTargetCellForMove(LudoGameCell ludoCell, int player, int length)
        {
            if (ludoCell == null) return null;
            LudoGameCell result = null;

            int count = (length >= 0 ? length : -length);
            GameMoveDirection direction = (length >= 0 ? GameMoveDirection.Next : GameMoveDirection.Prev);
            LudoGameCell currentCell = ludoCell;
            while (count != 0)
            {
                // Najdu vztažená políčka v daném směru:
                List<LudoGameCell> cells = this.FindLudoCellsLinkedToCell(currentCell, direction);

                // Vyberu si políčko, kam půjdu (pokud je mezi nimi můj domeček, půjdu do něj, jinak na běžné políčko):
                LudoGameCell cell = this.SelectOptimalLudoCell(currentCell, cells, player);
                if (cell == null)
                    break;

                // Políčko máme:
                currentCell = cell;
                count--;
                if (count == 0)
                    result = currentCell;
            }
            return result;
        }
        /// <summary>
        /// Vybere z řady dostupných políček to nejvhodnější
        /// </summary>
        /// <param name="currentCell"></param>
        /// <param name="cells"></param>
        /// <param name="player"></param>
        /// <returns></returns>
        protected LudoGameCell SelectOptimalLudoCell(LudoGameCell currentCell, List<LudoGameCell> cells, int player)
        {
            if (cells == null || cells.Count == 0) return null;

            LudoGameCell result;
            // 1. Je tam můj domeček?
            result = cells.FirstOrDefault(c => (c.CellType == LudoGameCellType.Target && c.Player == player));

            // 2. Není domeček. Je tam nějaké obecně dostupné políčko?
            if (result == null)
                result = cells.FirstOrDefault(c => (c.CellType == LudoGameCellType.Standard || c.CellType == LudoGameCellType.Start));

            return result;
        }
        #endregion
    }
    #endregion
    #region class LudoGamePlayer : třída, která v sobě zahrnuje všechny informace o jednom hráči
    /// <summary>
    /// LudoGamePlayer : třída, která v sobě zahrnuje všechny informace o jednom hráči
    /// </summary>
    public class LudoGamePlayer : GamePlayer
    {
        public LudoGamePlayer(LudoGame ludo, int player)
        {
            this.Ludo = ludo;
            this.Player = player;
            this.HomeCells = new List<LudoGameCell>();
            this.StartCell = null;
            this.TargetCells = new List<LudoGameCell>();
            this.Pines = new List<LudoGamePine>();
        }
        /// <summary>
        /// Reference na základnu (LudoGame)
        /// </summary>
        public LudoGame Ludo { get; private set; }
        /// <summary>
        /// Index hráče, koresponduje s hrací plochou
        /// </summary>
        public int Player { get; private set; }
        /// <summary>
        /// Pořadí hráče, v něm se střídají ve hře
        /// </summary>
        public int Order { get; set; }
        /// <summary>
        /// Status tohoto hráče, postupuje skrz různé stavy pomocí metod LudoGamePlayer.SetStatus() / ClearStatus(), testuje se přes HasStatus().
        /// </summary>
        public LudoGamePlayerStatus Status { get; set; }
        /// <summary>
        /// Objekt generátoru náhod
        /// </summary>
        public GameRand Rand { get; set; }
        /// <summary>
        /// Výchozí políčka = domeček
        /// </summary>
        public List<LudoGameCell> HomeCells { get; private set; }
        /// <summary>
        /// Startovní políčko
        /// </summary>
        public LudoGameCell StartCell { get; set; }
        /// <summary>
        /// Cílová políčka
        /// </summary>
        public List<LudoGameCell> TargetCells { get; private set; }
        /// <summary>
        /// Figurky
        /// </summary>
        public List<LudoGamePine> Pines { get; private set; }

        #region Status, testy a změny stavu
        /// <summary>
        /// Vrátí true, pokud tento player má daný stav.
        /// Pokud se testuje více hodnot stavu (stav je Flag), pak vrací true i při shodě jednoho z bitů.
        /// </summary>
        /// <param name="testStatus"></param>
        /// <returns></returns>
        public bool HasStatus(LudoGamePlayerStatus testStatus)
        {
            return (((int)this.Status & (int)testStatus) != 0);
        }
        /// <summary>
        /// Daný status nastaví / nuluje, podle hodnoty value
        /// </summary>
        /// <param name="status"></param>
        /// <param name="value"></param>
        public void SetStatusToValue(LudoGamePlayerStatus status, bool value)
        {
            if (value)
                this.SetStatus(status);
            else
                this.ResetStatus(status);
        }
        /// <summary>
        /// Nastaví daný status. Nastaví všechny předané bity.
        /// </summary>
        /// <param name="status"></param>
        public void SetStatus(LudoGamePlayerStatus status)
        {
            this.Status = (LudoGamePlayerStatus)(this.Status | status);
        }
        /// <summary>
        /// Nastaví daný status. Nastaví všechny předané bity.
        /// </summary>
        /// <param name="status"></param>
        public void ResetStatus(LudoGamePlayerStatus status)
        {
            int max = Int32.MaxValue;
            int res = (int)status ^ max;
            this.Status = (LudoGamePlayerStatus)((int)this.Status & res);
        }
        /// <summary>
        /// true, pokud tento hráč je právě nyní na tahu = má status IsActive
        /// </summary>
        public bool IsActive
        { 
            get { return this.HasStatus(LudoGamePlayerStatus.IsActive); }
            set { this.SetStatusToValue(LudoGamePlayerStatus.IsActive, value); }
        }
        /// <summary>
        /// true, pokud tento hráč je lidský hráč = má status IsHuman
        /// </summary>
        public bool IsHuman
        {
            get { return this.HasStatus(LudoGamePlayerStatus.IsHuman); }
            set { this.SetStatusToValue(LudoGamePlayerStatus.IsHuman, value); }
        }
        /// <summary>
        /// true, pokud tento hráč je počítač = má status IsAlien
        /// </summary>
        public bool IsAlien
        {
            get { return this.HasStatus(LudoGamePlayerStatus.IsAlien); }
            set { this.SetStatusToValue(LudoGamePlayerStatus.IsAlien, value); }
        }
        /// <summary>
        /// true, pokud tento hráč právě nyní hází kostkou = má status RollDice
        /// </summary>
        public bool IsRollDice
        {
            get { return this.HasStatus(LudoGamePlayerStatus.RollDice); }
            set { this.SetStatusToValue(LudoGamePlayerStatus.RollDice, value); }
        }
        /// <summary>
        /// true, pokud tento hráč právě nyní tahá figurkou = má status MovePine
        /// </summary>
        public bool IsMovePine
        {
            get { return this.HasStatus(LudoGamePlayerStatus.MovePine); }
            set { this.SetStatusToValue(LudoGamePlayerStatus.MovePine, value); }
        }
        /// <summary>
        /// true, pokud tento hráč je kompletně hotový = má status IsInTarget
        /// </summary>
        public bool IsInTarget
        {
            get { return this.HasStatus(LudoGamePlayerStatus.IsInTarget); }
            set { this.SetStatusToValue(LudoGamePlayerStatus.IsInTarget, value); }
        }
        /// <summary>
        /// true, pokud tento hráč může hrát = má status (IsHuman nebo IsAlien), a současně nemá stav IsInTarget
        /// </summary>
        public bool CanPlay
        { 
            get { return (this.IsHuman || this.IsAlien) && !this.IsInTarget; }
        }
        #endregion
    }
    #endregion
    #region class LudoGamePine : potomek třídy GamePine, uchovává data nad rámec bázové třídy
    /// <summary>
    /// LudoGamePine : potomek třídy GamePine, uchovává data nad rámec bázové třídy
    /// </summary>
    public class LudoGamePine : GamePine
    {
        public LudoGamePine(LudoGameCell homeCell, SizeF size)
        {
            int player = homeCell.Player;

            this.HomeCell = homeCell;
            this.Cell = homeCell;
            this.Player = player;
            this.Index = homeCell.Index;
            this.LogicalSize = size;
            this.OnHotBoundsExpandRatio = 0.21f;

            // Stavové obrázky:
            this.ImageNameStandard = LudoGame.ImageNamePineStandardPlayer(player);
            this.ImageNameOnMouse = LudoGame.ImageNamePineMousePlayer(player);
            this.ImageNameMoveOutOfCell = LudoGame.ImageNamePineMoveOutPlayer(player);
            this.ImageNameMoveCellOk = LudoGame.ImageNamePineMoveCellOkPlayer(player);
            this.ImageNameMoveCellWarning = LudoGame.ImageNamePineMoveCellWarningPlayer(player);
            this.ImageNameMoveCellError = LudoGame.ImageNamePineMoveCellErrorPlayer(player);

            // Aktuální obrázky:
            this.BackStdImageName = this.ImageNameStandard;
            this.BackStdImageNormal = this.ImageNameStandard;
        }
        #region Přemisťování a interaktivita figurky
        /// <summary>
        /// Zjistí, zda daná figurka v aktuálním stavu může reagovat na myš
        /// </summary>
        /// <returns></returns>
        protected override bool PineMouseIsEnabled()
        {
            return this.Ludo.IsPineCurrentActive(this);
        }
        /// <summary>
        /// Zjistí, zda danou figurkou je možno v aktuálním stavu hry pohybovat.
        /// </summary>
        /// <returns></returns>
        protected override bool PineMoveIsEnabled()
        {
            return this.Ludo.IsPineCurrentActive(this);
        }
        /// <summary>
        /// Metoda určí vztah figurky this při pohybu nad plochou,
        /// když se nachází nad daným políčkem (cell), a na tomto políčku se nacházejí určité figurky (pines).
        /// base metoda vrací implicitní výsledky.
        /// </summary>
        /// <param name="cell"></param>
        /// <param name="pines"></param>
        /// <returns></returns>
        protected override GameMoveResultType PineMoveGetResult(GameCell cell, List<GamePine> pines)
        {
            // Zjistím, zda figurka může na dané políčko.
            // V případě, kdy je figurka na tomto políčku (nebo mimo něj) uvolněna (při pohybu),
            //  pak se zde zjištěný výsledek (GameMoveResultType) následně předá do metody this.PineMoveFinalize().
            return this.Ludo.GetMoveResult(this, cell, pines);
        }
        #endregion
        #region Override property
        #endregion
        #region Specifické property
        /// <summary>
        /// Reference na základnu (LudoGame)
        /// </summary>
        protected LudoGame Ludo { get { return this.Owner as LudoGame; } }
        /// <summary>
        /// Políčko, na kterém figurka stojí, přetypované na políčko LudoGameCell
        /// </summary>
        public LudoGameCell LudoCell { get { return this.Cell as LudoGameCell; } }
        /// <summary>
        /// Výchozí políčko, odkud se začíná (a kam se vrací po vyhození)
        /// </summary>
        public LudoGameCell HomeCell { get; private set; }
        /// <summary>
        /// Obrázek figurky základní (záloha)
        /// </summary>
        protected string BackStdImageNormal { get; private set; }
        #endregion
    }
    #endregion
    #region class LudoGameCell : potomek třídy GameCell, uchovává data nad rámec bázové třídy
    /// <summary>
    /// LudoGameCell : potomek třídy GameCell, uchovává data nad rámec bázové třídy
    /// </summary>
    public class LudoGameCell : GameCell
    {
        #region Konstruktory
        public LudoGameCell(LudoGameCellType cellType, int player, int index, PointF center, float s, Color backColor, string imageCell)
        {
            this.CellType = cellType;
            this.Player = player;
            this.Index = index;
            this.LogicalCenter = center;
            this.LogicalSize = new SizeF(s, s);
            this.BackStdColor = backColor;
            this.BackStdImageName = imageCell;
            this.OnHotBoundsExpandRatio = 0.05f;
        }
        public LudoGameCell(LudoGameCellType cellType, int player, int index, float x, float y, float s, Color backColor, string imageCell)
        {
            this.CellType = cellType;
            this.Player = player;
            this.Index = index;
            this.LogicalCenter = new PointF(x, y);
            this.LogicalSize = new SizeF(s, s);
            this.BackStdColor = backColor;
            this.BackStdImageName = imageCell;
            this.OnHotBoundsExpandRatio = 0.05f;
        }
        #endregion
        #region Specifické property
        /// <summary>
        /// Druh políčka
        /// </summary>
        public LudoGameCellType CellType { get; private set; }
        #endregion
    }
    #endregion
    #region class LudoGameLink : potomek třídy GameLink, uchovává data nad rámec bázové třídy
    /// <summary>
    /// LudoGameLink : potomek třídy GameLink, uchovává data nad rámec bázové třídy
    /// </summary>
    public class LudoGameLink : GameLink
    {
        #region Specifické property
        /// <summary>
        /// Reference na základnu (LudoGame)
        /// </summary>
        protected LudoGame Ludo { get { return this.Owner as LudoGame; } }
        /// <summary>
        /// Políčko ve vztahu 1 (předchozí), přetypované na políčko LudoGameCell
        /// </summary>
        public LudoGameCell LudoCell1 { get { return this.Cell1 as LudoGameCell; } }
        /// <summary>
        /// Políčko ve vztahu 2 (následující), přetypované na políčko LudoGameCell
        /// </summary>
        public LudoGameCell LudoCell2 { get { return this.Cell2 as LudoGameCell; } }
        #endregion
    }
    #endregion
    #region Enumy pro Ludo : LudoGameCellType
    public enum LudoGameAreaType
    {
        None = 0,
        Ludo4a,
        Ludo4b,
        Ludo6a,
        Ludo6b,
        Ludo6c,
        Ludo6d,
        LudoExcentric
    }
    /// <summary>
    /// Druhy políček na hrací ploše
    /// </summary>
    public enum LudoGameCellType
    {
        None = 0,
        Home,
        Start,
        Standard,
        Target
    }
    /// <summary>
    /// Stavy hráče, příznaky
    /// </summary>
    [Flags]
    public enum LudoGamePlayerStatus
    {
        /// <summary>
        /// Nehraje
        /// </summary>
        NoPlay = 0,
        /// <summary>
        /// Je to člověk, hraje myší
        /// </summary>
        IsHuman = 1,
        /// <summary>
        /// Je to mimozemšťan, hraje za něj počítač
        /// </summary>
        IsAlien = IsHuman << 1,
        /// <summary>
        /// Nyní je na tahu, setrvává v tomto stavu dokud se neposune na dalšího hráče (to může být Human i Alien)
        /// </summary>
        IsActive = IsAlien << 1,
        /// <summary>
        /// Nyní hází kostkou (to může být Human i Alien)
        /// </summary>
        RollDice = IsActive << 1,
        /// <summary>
        /// Táhne figurkou (to může být Human i Alien)
        /// </summary>
        MovePine = RollDice << 1,
        /// <summary>
        /// Je celý v cíli, už nehraje
        /// </summary>
        IsInTarget = MovePine << 1,
    }
    #endregion
}
